import asyncio
import os
import aiohttp
from typing import Optional, Dict, Any, Callable
from datetime import datetime
from urllib.parse import urlparse

from database.mysql.video_crud import VideoTaskCRUD
from database.mysql.video_model import VideoTask, TaskStatus
from utils.ffmpeg_helper import FFmpegHelper
from utils.file_helper import FileHelper
from utils.qiniu_helper import QiniuHelper
from utils.request_context import RequestContext, context_logger
from config.video_config import VIDEO_CONFIG
from config.logging_config import ProjectLoggers

logger = ProjectLoggers.get_service_logger()

class VideoAudioService:
    """视频转音频服务类"""
    
    def __init__(self):
        self.processing_tasks = set()  # 正在处理的任务集合
        self.task_semaphore = asyncio.Semaphore(VIDEO_CONFIG['max_concurrent_tasks'])
    
    async def upload_and_create_task(
        self,
        file_content: bytes,
        filename: str,
        user_openid: Optional[str] = None,
        audio_format: str = None
    ) -> Dict[str, Any]:
        """上传视频文件并创建处理任务"""
        try:
            # 验证文件格式
            if not FFmpegHelper.validate_video_format(filename):
                return {
                    "success": False,
                    "error": f"不支持的视频格式，支持的格式: {', '.join(VIDEO_CONFIG['supported_video_formats'])}"
                }
            
            # 验证文件大小
            if not FileHelper.validate_file_size(len(file_content)):
                max_size = FileHelper.format_file_size(VIDEO_CONFIG['max_file_size'])
                return {
                    "success": False,
                    "error": f"文件大小超过限制，最大支持: {max_size}"
                }
            
            # 验证音频格式
            if audio_format and not FFmpegHelper.validate_audio_format(audio_format):
                return {
                    "success": False,
                    "error": f"不支持的音频格式，支持的格式: {', '.join(VIDEO_CONFIG['supported_audio_formats'])}"
                }
            
            # 保存上传的文件
            temp_video_path = await FileHelper.save_uploaded_file(file_content, filename)
            
            try:
                # 获取视频信息
                video_info = await FFmpegHelper.get_video_info(temp_video_path)
                if not video_info:
                    FileHelper.delete_file(temp_video_path)
                    return {"success": False, "error": "无法解析视频文件，文件可能已损坏"}
                
                # 上传原视频到七牛云
                safe_filename = FileHelper.get_safe_filename(filename)
                video_key = f"videos/{datetime.now().strftime('%Y%m%d')}/{safe_filename}"
                
                video_upload_result = QiniuHelper.upload_file_by_path(temp_video_path, video_key)
                
                if not video_upload_result['success']:
                    FileHelper.delete_file(temp_video_path)
                    return {"success": False, "error": f"视频上传失败: {video_upload_result['error']}"}
                
                video_url = video_upload_result['url']
                
                # 创建数据库任务记录
                task = await VideoTaskCRUD.create_task(
                    original_filename=filename,
                    user_openid=user_openid,
                    file_size=len(file_content),
                    video_format=FileHelper.get_file_extension(filename),
                    video_url=video_url,
                    duration=video_info['duration']
                )
                
                # 获取当前请求ID，传递给异步任务
                current_request_id = RequestContext.get_request_id()
                
                # 启动异步处理任务
                asyncio.create_task(self._process_video_task(
                    task.task_id,
                    temp_video_path,
                    audio_format or VIDEO_CONFIG['default_output_format'],
                    'high',
                    current_request_id  # 传递请求ID
                ))
                
                context_logger.info(f"🚀 异步视频处理任务已启动 - 任务ID: {task.task_id}")
                
                return {
                    "success": True,
                    "data": {
                        "task_id": task.task_id,
                        "original_filename": filename,
                        "file_size": FileHelper.format_file_size(len(file_content)),
                        "duration": video_info['duration'],
                        "video_url": video_url,
                        "status": TaskStatus.PENDING.value,
                        "created_at": task.created_at.isoformat()
                    }
                }
                
            except Exception as e:
                # 清理临时文件
                FileHelper.delete_file(temp_video_path)
                raise e
                
        except Exception as e:
            logger.error(f"上传和创建任务失败: {str(e)}")
            return {"success": False, "error": f"处理失败: {str(e)}"}
    
    async def create_task_from_url(
        self,
        video_url: str,
        original_filename: str,
        user_openid: Optional[str] = None,
        audio_format: str = None,
        audio_quality: str = None
    ) -> Dict[str, Any]:
        """通过视频URL创建处理任务"""
        try:
            # 验证文件格式
            if not FFmpegHelper.validate_video_format(original_filename):
                return {
                    "success": False,
                    "error": f"不支持的视频格式，支持的格式: {', '.join(VIDEO_CONFIG['supported_video_formats'])}"
                }
            
            # 验证音频格式
            if audio_format and not FFmpegHelper.validate_audio_format(audio_format):
                return {
                    "success": False,
                    "error": f"不支持的音频格式，支持的格式: {', '.join(VIDEO_CONFIG['supported_audio_formats'])}"
                }
            
            # 验证URL格式
            if not self._validate_url(video_url):
                return {"success": False, "error": "无效的视频链接"}
            
            # 获取视频文件信息（通过HEAD请求）
            file_info = await self._get_file_info_from_url(video_url)
            if not file_info["success"]:
                return {"success": False, "error": f"无法获取视频文件信息: {file_info['error']}"}
            
            file_size = file_info["file_size"]
            
            # 验证文件大小
            if not FileHelper.validate_file_size(file_size):
                max_size = FileHelper.format_file_size(VIDEO_CONFIG['max_file_size'])
                return {
                    "success": False,
                    "error": f"文件大小超过限制，最大支持: {max_size}"
                }
            
            # 创建数据库任务记录
            task = await VideoTaskCRUD.create_task(
                original_filename=original_filename,
                user_openid=user_openid,
                file_size=file_size,
                video_format=FileHelper.get_file_extension(original_filename),
                video_url=video_url
            )
            
            # 获取当前请求ID，传递给异步任务
            current_request_id = RequestContext.get_request_id()
            
            # 启动异步处理任务
            asyncio.create_task(self._process_video_task_from_url(
                task.task_id,
                video_url,
                original_filename,
                audio_format or VIDEO_CONFIG['default_output_format'],
                audio_quality or 'high',
                current_request_id  # 传递请求ID
            ))
            
            context_logger.info(f"🚀 异步视频处理任务已启动 - 任务ID: {task.task_id}")
            
            return {
                "success": True,
                "data": {
                    "task_id": task.task_id,
                    "original_filename": original_filename,
                    "file_size": FileHelper.format_file_size(file_size),
                    "video_url": video_url,
                    "status": TaskStatus.PENDING.value,
                    "created_at": task.created_at.isoformat()
                }
            }
            
        except Exception as e:
            logger.error(f"通过URL创建任务失败: {str(e)}")
            return {"success": False, "error": f"处理失败: {str(e)}"}
    
    async def _process_video_task(
        self,
        task_id: str,
        video_path: str,
        audio_format: str,
        audio_quality: str = 'high',
        current_request_id: str = None
    ):
        """处理视频转音频任务"""
        async with self.task_semaphore:
            # 设置请求上下文，确保异步任务中也有请求ID
            if current_request_id:
                RequestContext.set_request_id(current_request_id)
            
            if task_id in self.processing_tasks:
                context_logger.warning(f"⚠️ 任务已在处理中: {task_id}")
                return
            
            self.processing_tasks.add(task_id)
            
            try:
                # 更新任务状态为处理中
                await VideoTaskCRUD.update_task_status(
                    task_id, TaskStatus.PROCESSING, progress=0
                )
                
                context_logger.info(f"🎬 开始处理视频转音频任务 - 任务ID: {task_id}, 视频: {os.path.basename(video_path)}")
                
                # 生成音频输出路径
                audio_filename = FFmpegHelper.get_temp_filename(
                    os.path.basename(video_path), audio_format
                )
                
                # 更新进度：开始音频提取
                await VideoTaskCRUD.update_task_status(
                    task_id, TaskStatus.PROCESSING, progress=25
                )
                
                context_logger.info(f"🎵 开始音频提取 - 任务ID: {task_id}, 格式: {audio_format}, 质量: {audio_quality}")
                # 提取音频
                success = await FFmpegHelper.extract_audio(
                    video_path=video_path,
                    output_path=audio_filename,
                    audio_format=audio_format,
                    audio_quality=audio_quality
                )
                
                if not success:
                    # 检查是否是因为没有音频轨道导致的失败
                    video_info = await FFmpegHelper.get_video_info(video_path)
                    if video_info:
                        has_audio = len(video_info.get('audio_streams', [])) > 0
                        if not has_audio:
                            error_message = "该视频文件不包含音频轨道，无法提取音频。请选择包含音频的视频文件。"
                        else:
                            error_message = "音频提取失败，请检查视频文件是否完整或格式是否受支持。"
                    else:
                        error_message = "音频提取失败，视频文件可能已损坏。"
                    
                    context_logger.error(f"❌ 音频提取失败 - 任务ID: {task_id}, 原因: {error_message}")
                    await VideoTaskCRUD.update_task_status(
                        task_id, TaskStatus.FAILED, error_message=error_message
                    )
                    return
                
                context_logger.info(f"✅ 音频提取成功 - 任务ID: {task_id}")
                
                # 更新进度：音频提取完成
                await VideoTaskCRUD.update_task_status(
                    task_id, TaskStatus.PROCESSING, progress=75
                )
                
                # 上传音频文件到七牛云
                base_name = FileHelper.get_file_basename(os.path.basename(video_path))
                audio_key = f"audios/{datetime.now().strftime('%Y%m%d')}/{base_name}.{audio_format}"
                
                context_logger.info(f"☁️ 开始上传音频文件到七牛云 - 任务ID: {task_id}, Key: {audio_key}")
                audio_upload_result = QiniuHelper.upload_file_by_path(audio_filename, audio_key)
                
                if not audio_upload_result['success']:
                    error_msg = f"音频上传失败: {audio_upload_result['error']}"
                    context_logger.error(f"❌ {error_msg} - 任务ID: {task_id}")
                    await VideoTaskCRUD.update_task_status(
                        task_id, TaskStatus.FAILED, 
                        error_message=error_msg
                    )
                    return
                
                audio_url = audio_upload_result['url']
                context_logger.info(f"✅ 音频文件上传成功 - 任务ID: {task_id}, URL: {audio_url}")
                
                # 更新任务状态为完成
                await VideoTaskCRUD.update_task_urls(
                    task_id,
                    audio_url=audio_url,
                    audio_format=audio_format
                )
                
                await VideoTaskCRUD.update_task_status(
                    task_id, TaskStatus.COMPLETED, progress=100
                )
                
                context_logger.info(f"🎉 视频转音频任务完成 - 任务ID: {task_id}")
                
            except asyncio.TimeoutError:
                error_msg = "处理超时"
                context_logger.error(f"⏰ 任务处理超时 - 任务ID: {task_id}")
                await VideoTaskCRUD.update_task_status(
                    task_id, TaskStatus.FAILED, error_message=error_msg
                )
                
            except Exception as e:
                error_msg = str(e)
                context_logger.error(f"❌ 任务处理失败 - 任务ID: {task_id}, 错误: {error_msg}", exc_info=True)
                await VideoTaskCRUD.update_task_status(
                    task_id, TaskStatus.FAILED, error_message=error_msg
                )
                
            finally:
                # 清理临时文件
                if video_path and os.path.exists(video_path):
                    FileHelper.delete_file(video_path)
                    context_logger.info(f"🧹 临时视频文件清理完成 - 任务ID: {task_id}")
                
                if 'audio_filename' in locals() and os.path.exists(audio_filename):
                    FileHelper.delete_file(audio_filename)
                    context_logger.info(f"🧹 临时音频文件清理完成 - 任务ID: {task_id}")
                
                # 从处理中任务集合移除
                self.processing_tasks.discard(task_id)
    
    async def _process_video_task_from_url(
        self,
        task_id: str,
        video_url: str,
        original_filename: str,
        audio_format: str,
        audio_quality: str = 'high',
        current_request_id: str = None
    ):
        """处理从URL下载的视频转音频任务"""
        async with self.task_semaphore:
            # 设置请求上下文，确保异步任务中也有请求ID
            if current_request_id:
                RequestContext.set_request_id(current_request_id)
            
            if task_id in self.processing_tasks:
                context_logger.warning(f"⚠️ 任务已在处理中: {task_id}")
                return
            
            self.processing_tasks.add(task_id)
            temp_video_path = None
            
            try:
                # 更新任务状态为处理中
                await VideoTaskCRUD.update_task_status(
                    task_id, TaskStatus.PROCESSING, progress=0
                )
                
                logger.info(f"开始处理视频转音频任务: {task_id}")
                
                # 下载视频文件
                await VideoTaskCRUD.update_task_status(
                    task_id, TaskStatus.PROCESSING, progress=10
                )
                
                download_result = await self._download_video_from_url(video_url, original_filename)
                if not download_result["success"]:
                    await VideoTaskCRUD.update_task_status(
                        task_id, TaskStatus.FAILED, error_message=f"视频下载失败: {download_result['error']}"
                    )
                    return
                
                temp_video_path = download_result["file_path"]
                
                # 获取视频信息并更新数据库
                await VideoTaskCRUD.update_task_status(
                    task_id, TaskStatus.PROCESSING, progress=20
                )
                
                video_info = await FFmpegHelper.get_video_info(temp_video_path)
                if video_info:
                    await VideoTaskCRUD.update_task_urls(
                        task_id,
                        duration=video_info['duration']
                    )
                
                # 生成音频输出路径
                audio_filename = FFmpegHelper.get_temp_filename(
                    os.path.basename(temp_video_path), audio_format
                )
                
                # 更新进度：开始音频提取
                await VideoTaskCRUD.update_task_status(
                    task_id, TaskStatus.PROCESSING, progress=40
                )
                
                # 提取音频
                success = await FFmpegHelper.extract_audio(
                    video_path=temp_video_path,
                    output_path=audio_filename,
                    audio_format=audio_format,
                    audio_quality=audio_quality
                )
                
                if not success:
                    await VideoTaskCRUD.update_task_status(
                        task_id, TaskStatus.FAILED, error_message="音频提取失败"
                    )
                    return
                
                # 更新进度：音频提取完成
                await VideoTaskCRUD.update_task_status(
                    task_id, TaskStatus.PROCESSING, progress=80
                )
                
                # 上传音频文件到七牛云
                base_name = FileHelper.get_file_basename(original_filename)
                audio_key = f"audios/{datetime.now().strftime('%Y%m%d')}/{base_name}.{audio_format}"
                
                audio_upload_result = QiniuHelper.upload_file_by_path(audio_filename, audio_key)
                
                if not audio_upload_result['success']:
                    await VideoTaskCRUD.update_task_status(
                        task_id, TaskStatus.FAILED, 
                        error_message=f"音频上传失败: {audio_upload_result['error']}"
                    )
                    return
                
                audio_url = audio_upload_result['url']
                
                # 更新任务状态为完成
                await VideoTaskCRUD.update_task_urls(
                    task_id,
                    audio_url=audio_url,
                    audio_format=audio_format
                )
                
                await VideoTaskCRUD.update_task_status(
                    task_id, TaskStatus.COMPLETED, progress=100
                )
                
                logger.info(f"视频转音频任务完成: {task_id}")
                
            except asyncio.TimeoutError:
                await VideoTaskCRUD.update_task_status(
                    task_id, TaskStatus.FAILED, error_message="处理超时"
                )
                logger.error(f"任务处理超时: {task_id}")
                
            except Exception as e:
                await VideoTaskCRUD.update_task_status(
                    task_id, TaskStatus.FAILED, error_message=str(e)
                )
                logger.error(f"任务处理失败 {task_id}: {str(e)}")
                
            finally:
                # 清理临时文件
                if temp_video_path and os.path.exists(temp_video_path):
                    FileHelper.delete_file(temp_video_path)
                
                if 'audio_filename' in locals() and os.path.exists(audio_filename):
                    FileHelper.delete_file(audio_filename)
                
                # 从处理中任务集合移除
                self.processing_tasks.discard(task_id)
    
    def _validate_url(self, url: str) -> bool:
        """验证URL格式"""
        try:
            result = urlparse(url)
            return all([result.scheme, result.netloc])
        except Exception:
            return False
    
    async def _get_file_info_from_url(self, url: str) -> Dict[str, Any]:
        """从URL获取文件信息"""
        try:
            timeout = aiohttp.ClientTimeout(total=30)
            async with aiohttp.ClientSession(timeout=timeout) as session:
                async with session.head(url) as response:
                    if response.status == 200:
                        content_length = response.headers.get('content-length')
                        if content_length:
                            file_size = int(content_length)
                            return {
                                "success": True,
                                "file_size": file_size
                            }
                        else:
                            return {"success": False, "error": "无法获取文件大小"}
                    else:
                        return {"success": False, "error": f"HTTP状态码: {response.status}"}
        except Exception as e:
            logger.error(f"获取文件信息失败: {str(e)}")
            return {"success": False, "error": str(e)}
    
    async def _download_video_from_url(self, url: str, filename: str) -> Dict[str, Any]:
        """从URL下载视频文件"""
        try:
            # 生成临时文件路径
            temp_filepath = FileHelper.generate_unique_filename(filename, "download")
            temp_filepath = os.path.join(VIDEO_CONFIG['temp_dir'], temp_filepath)
            
            timeout = aiohttp.ClientTimeout(total=300)  # 5分钟超时
            async with aiohttp.ClientSession(timeout=timeout) as session:
                async with session.get(url) as response:
                    if response.status == 200:
                        # 分块下载文件
                        with open(temp_filepath, 'wb') as f:
                            async for chunk in response.content.iter_chunked(8192):
                                f.write(chunk)
                        
                        logger.info(f"视频文件下载成功: {temp_filepath}")
                        return {
                            "success": True,
                            "file_path": temp_filepath
                        }
                    else:
                        return {"success": False, "error": f"下载失败，HTTP状态码: {response.status}"}
                        
        except Exception as e:
            logger.error(f"下载视频文件失败: {str(e)}")
            return {"success": False, "error": str(e)}
    
    async def get_task_status(self, task_id: str) -> Dict[str, Any]:
        """获取任务状态"""
        try:
            task = await VideoTaskCRUD.get_task_by_id(task_id)
            if not task:
                return {"success": False, "error": "任务不存在"}
            
            return {
                "success": True,
                "data": {
                    "task_id": task.task_id,
                    "original_filename": task.original_filename,
                    "status": task.status,
                    "progress": task.progress,
                    "video_url": task.video_url,
                    "audio_url": task.audio_url,
                    "duration": task.duration,
                    "file_size": FileHelper.format_file_size(task.file_size) if task.file_size else None,
                    "audio_format": task.audio_format,
                    "error_message": task.error_message,
                    "created_at": task.created_at.isoformat(),
                    "started_at": task.started_at.isoformat() if task.started_at else None,
                    "completed_at": task.completed_at.isoformat() if task.completed_at else None
                }
            }
            
        except Exception as e:
            logger.error(f"获取任务状态失败 {task_id}: {str(e)}")
            return {"success": False, "error": str(e)}
    
    async def get_user_tasks(
        self, 
        user_openid: str, 
        page: int = 1, 
        page_size: int = 20
    ) -> Dict[str, Any]:
        """获取用户的任务列表（分页）"""
        try:
            # 验证分页参数
            if page < 1:
                page = 1
            if page_size < 1 or page_size > 100:  # 限制每页最多100条
                page_size = 20
            
            tasks, total_count = await VideoTaskCRUD.get_tasks_by_user(
                user_openid, page, page_size
            )
            
            task_list = []
            for task in tasks:
                task_list.append({
                    "task_id": task.task_id,
                    "original_filename": task.original_filename,
                    "status": task.status,
                    "progress": task.progress,
                    "video_url": task.video_url,
                    "audio_url": task.audio_url,
                    "duration": task.duration,
                    "file_size": FileHelper.format_file_size(task.file_size) if task.file_size else None,
                    "audio_format": task.audio_format,
                    "created_at": task.created_at.isoformat(),
                    "completed_at": task.completed_at.isoformat() if task.completed_at else None
                })
            
            # 计算分页信息
            total_pages = (total_count + page_size - 1) // page_size
            has_next = page < total_pages
            has_prev = page > 1
            
            return {
                "success": True,
                "data": {
                    "tasks": task_list,
                    "pagination": {
                        "page": page,
                        "page_size": page_size,
                        "total": total_count,
                        "total_pages": total_pages,
                        "has_next": has_next,
                        "has_prev": has_prev
                    }
                }
            }
            
        except Exception as e:
            context_logger.error(f"获取用户任务失败 {user_openid}: {str(e)}")
            return {"success": False, "error": str(e)}
    
    async def cancel_task(self, task_id: str) -> Dict[str, Any]:
        """取消任务"""
        try:
            task = await VideoTaskCRUD.get_task_by_id(task_id)
            if not task:
                return {"success": False, "error": "任务不存在"}
            
            if task.status in [TaskStatus.COMPLETED.value, TaskStatus.FAILED.value, TaskStatus.CANCELLED.value]:
                return {"success": False, "error": "任务无法取消"}
            
            # 更新任务状态
            await VideoTaskCRUD.update_task_status(
                task_id, TaskStatus.CANCELLED, error_message="用户取消"
            )
            
            # 从处理队列中移除
            self.processing_tasks.discard(task_id)
            
            logger.info(f"任务取消成功: {task_id}")
            return {"success": True, "message": "任务已取消"}
            
        except Exception as e:
            logger.error(f"取消任务失败 {task_id}: {str(e)}")
            return {"success": False, "error": str(e)}
    
    async def delete_task(self, task_id: str) -> Dict[str, Any]:
        """删除任务"""
        try:
            task = await VideoTaskCRUD.get_task_by_id(task_id)
            if not task:
                return {"success": False, "error": "任务不存在"}
            
            # 先取消任务（如果正在处理）
            if task.status in [TaskStatus.PENDING.value, TaskStatus.PROCESSING.value]:
                await self.cancel_task(task_id)
            
            # 删除数据库记录
            success = await VideoTaskCRUD.delete_task(task_id)
            if success:
                logger.info(f"任务删除成功: {task_id}")
                return {"success": True, "message": "任务已删除"}
            else:
                return {"success": False, "error": "删除任务失败"}
                
        except Exception as e:
            logger.error(f"删除任务失败 {task_id}: {str(e)}")
            return {"success": False, "error": str(e)} 